{*********************************************************************
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * Autor: Brovin Y.D.
 * E-mail: y.brovin@gmail.com
 *
 ********************************************************************}

unit FGX.ProgressDialog;

interface

uses
  System.Classes, FGX.ProgressDialog.Types;

type

  IFGXProgressDialogService = interface;

  /// <summary>
  ///   Generic Abstract base class provide base functionality for creation and using progress/activity dialog.
  ///   Each dialog has Message and Title and holds instance of wrapper native dialog.
  /// </summary>
  TfgCustomProcessingDialog<T: TfgNativeDialog> = class abstract (TComponent)
  private
    FNativeProgressDialog: T;
    FTitle: string;
    FMessage: string;
    procedure SetMessage(const Value: string);
    procedure SetTitle(const Value: string);
  protected
    /// <summary>
    ///   Returning a instance of wrapper native dialog. You should override this method for using
    ///   custom native dialog.
    /// </summary>
    function CreateNativeDialog: T; virtual; abstract;
    /// <summary>
    ///   Way for perform additional initialization before showing dialog
    /// </summary>
    procedure DoBeforeShow; virtual;
    property NativeDialog: T read FNativeProgressDialog;
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
    /// <summary>
    ///   Return Does current platform has implementation of native dialog
    /// </summary>
    function Supported: Boolean;
    procedure Show; virtual;
    procedure Hide; virtual;
  public
    property Message: string read FMessage write SetMessage;
    property Title: string read FTitle write SetTitle;
  end;

  { TfgActivityDialog }

  TfgCustomActivityDialog = class (TfgCustomProcessingDialog<TfgNativeActivityDialog>)
  protected
    function CreateNativeDialog: TfgNativeActivityDialog; override;
  end;

  /// <summary>
  ///   Native Modal dialog with activity indicator, title and message
  /// </summary>
  [ComponentPlatformsAttribute(pidAndroid or pidiOSDevice or pidiOSSimulator)]
  TfgActivityDialog = class (TfgCustomActivityDialog)
  published
    property Message;
    property Title;
  end;

  { TfgProgressDialog }

  TfgCustomProgressDialog = class (TfgCustomProcessingDialog<TfgNativeProgressDialog>)
  private
    FKind: TfgProgressDialogKind;
    FProgress: Single;
    procedure SetKind(const Value: TfgProgressDialogKind);
    procedure SetProgress(const Value: Single);
  protected
    function CreateNativeDialog: TfgNativeProgressDialog; override;
  public
    constructor Create(AOwner: TComponent); override;
    property Kind: TfgProgressDialogKind read FKind write SetKind default TfgProgressDialogKind.Determinated;
    /// <summary>
    ///    Current progress value of dialog in range [0..100]
    /// </summary>
    property Progress: Single read FProgress write SetProgress;
  end;

  /// <summary>
  ///   <para>
  ///     Native  Modal dialog with progress bar, title and message
  ///   </para>
  ///   <note type="note">
  ///     <list type="table">
  ///       <item>
  ///         <term>iOS</term>
  ///         <description>Doesn't support <see cref="TfgProgressDialog.Kind">Kind</see> property</description>
  ///       </item>
  ///       <item>
  ///         <term>Android</term>
  ///         <description>All property is supported</description>
  ///       </item>
  ///     </list>
  ///   </note>
  /// </summary>
  [ComponentPlatformsAttribute(pidAndroid or pidiOSDevice or pidiOSSimulator)]
  TfgProgressDialog = class (TfgCustomProgressDialog)
  published
    property Kind;
    property Message;
    property Progress;
    property Title;
  end;

  { IFGXProgressDialogService }

  /// <summary>
  ///   Factory for creation native progress and activity dialogs
  /// </summary>
  IFGXProgressDialogService = interface
  ['{83B4CDCD-5F4F-4ABA-A3BC-1836072312F2}']
    function CreateNativeProgressDialog: TfgNativeProgressDialog;
    function CreateNativeActivityDialog: TfgNativeActivityDialog;
  end;

implementation

uses
  System.Math, FMX.Types, FMX.Platform
{$IFDEF IOS}
   , FGX.ProgressDialog.iOS
{$ENDIF}
{$IFDEF ANDROID}
   , FGX.ProgressDialog.Android
{$ENDIF}
;

{ TfgCustomProgressDialog }

constructor TfgCustomProcessingDialog<T>.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  FNativeProgressDialog := CreateNativeDialog;
end;

destructor TfgCustomProcessingDialog<T>.Destroy;
begin
  if Supported then
    FNativeProgressDialog.Free;
  inherited Destroy;
end;

procedure TfgCustomProcessingDialog<T>.DoBeforeShow;
begin
  FNativeProgressDialog.Message := Message;
  FNativeProgressDialog.Title := Title;
end;

procedure TfgCustomProcessingDialog<T>.Hide;
begin
  if Supported then
    FNativeProgressDialog.Hide;
end;

procedure TfgCustomProcessingDialog<T>.SetMessage(const Value: string);
begin
  if Message <> Value then
  begin
    FMessage := Value;
    if Supported then
      FNativeProgressDialog.Message := Message;
  end;
end;

procedure TfgCustomProcessingDialog<T>.SetTitle(const Value: string);
begin
  if Title <> Value then
  begin
    FTitle := Value;
    if Supported then
      FNativeProgressDialog.Title := Title;
  end;
end;

procedure TfgCustomProcessingDialog<T>.Show;
begin
  if Supported then
  begin
    DoBeforeShow;
    FNativeProgressDialog.Show;
  end;
end;

function TfgCustomProcessingDialog<T>.Supported: Boolean;
begin
  Result := FNativeProgressDialog <> nil;
end;

{ TfgCustomActivityDialog }

function TfgCustomActivityDialog.CreateNativeDialog: TfgNativeActivityDialog;
var
  ProgressService: IFGXProgressDialogService;
begin
  if TPlatformServices.Current.SupportsPlatformService(IFGXProgressDialogService, IInterface(ProgressService)) then
    Result := ProgressService.CreateNativeActivityDialog
  else
    Result := nil;
end;

{ TfgCustomProgressDialog }

constructor TfgCustomProgressDialog.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  FKind := TfgProgressDialogKind.Determinated;
end;

function TfgCustomProgressDialog.CreateNativeDialog: TfgNativeProgressDialog;
var
  ProgressService: IFGXProgressDialogService;
begin
  if TPlatformServices.Current.SupportsPlatformService(IFGXProgressDialogService, IInterface(ProgressService)) then
    Result := ProgressService.CreateNativeProgressDialog
  else
    Result := nil;
end;

procedure TfgCustomProgressDialog.SetKind(const Value: TfgProgressDialogKind);
begin
  if Kind <> Value then
  begin
    FKind := Value;
    if Supported then
      NativeDialog.Kind := Kind;
  end;
end;

procedure TfgCustomProgressDialog.SetProgress(const Value: Single);
begin
  Assert(InRange(Value, 0, 100), 'Progress value must be in range [0..100]');

  if not SameValue(Progress, Value) then
  begin
    FProgress := Max(0, Min(100, Value));
    if Supported then
      NativeDialog.Progress := Progress;
  end;
end;

initialization
  RegisterFmxClasses([TfgCustomActivityDialog, TfgActivityDialog,
    TfgCustomProgressDialog, TfgProgressDialog]);

{$IF Defined(ANDROID) OR Defined(IOS)}
  RegisterService;
{$ENDIF}
end.
